"""Managed tool install/remove jobs — the Blender ops.py twin, minus operators.

Guards mirror the Blender panel exactly (single job, availability, entitlement,
credential). The download + verify runs on a worker thread; the atomic swap and
`register()` happen back on the main thread inside the dialog's CoreMessage
drain — the only place Cinema 4D allows imports that build UI.
"""

from __future__ import annotations

import tempfile
from pathlib import Path

from . import auth, catalog, events, host, manager
from ._core import api, config

JOB = {"addon_id": None, "message": ""}


def start_install(addon_id: str) -> tuple[bool, str]:
    """Begin download+verify for one catalog entry. (ok, error-message)."""
    if JOB["addon_id"]:
        return False, "Another Three Blocks tool is already installing"
    entry = catalog.addon(addon_id)
    if not entry or not entry.get("available") or not entry.get("entitled"):
        return False, "This tool is not available for this account"
    credential = config.load_credential(host.site_url())
    if not credential:
        return False, "Sign in before installing tools"
    if not host.online_allowed():
        return False, "Online access is disabled"

    frozen = dict(entry)
    site = host.site_url()
    token = credential["token"]
    JOB.update(addon_id=addon_id, message=f"Downloading {entry.get('displayName', 'tool')}…")

    def worker():
        try:
            with tempfile.TemporaryDirectory(prefix="three-blocks-addon-") as directory:
                archive = Path(directory) / "addon.zip"
                api.download_artifact(site, frozen["artifact"], token, archive)
                staged = manager.prepare_archive(frozen, archive)
            events.push("install", ("ready", frozen, str(staged)))
        except Exception as error:
            events.push("install", ("error", frozen, str(error)))

    events.spawn(worker)
    return True, ""


def finish_install(item: tuple) -> tuple[bool, str]:
    """Main-thread half: authorization re-check → atomic swap → register().

    Returns (ok, user-message). Call from the CoreMessage drain only.
    """
    kind, entry, payload = item
    name = entry.get("displayName") or entry.get("module") or "tool"
    try:
        if kind == "error":
            return False, payload
        # Re-check after the download, before code activates: the credential
        # may be gone or the plan verdict may have flipped mid-download.
        if not config.load_credential(host.site_url()) or (
            auth.VERDICT is not None and not auth.VERDICT.get("entitled")
        ):
            manager.discard_staged(payload)
            return False, "Tool install authorization changed"
        try:
            manager.activate(entry, payload)
        except Exception as error:
            return False, str(error)
        return True, f"Installed {name}"
    finally:
        JOB.update(addon_id=None, message="")


def remove_tool(addon_id: str, module: str | None) -> tuple[bool, str]:
    """Unload and delete an installed managed tool (confirm happens in the UI)."""
    entry = catalog.addon(addon_id)
    module = module or (entry or {}).get("module")
    if not module or not manager.installed(module):
        return False, "Tool is not installed"
    try:
        manager.remove(module)
    except Exception as error:
        return False, str(error)
    name = (entry or {}).get("displayName") or module
    return True, f"Removed {name}"
